home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Toolbox / ModalList / ModalList.c next >
Encoding:
C/C++ Source or Header  |  1994-11-18  |  18.5 KB  |  617 lines  |  [TEXT/MPS ]

  1. /*
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    ModalList : Simple Modal Dialog and List Manager Sample Application
  6. #
  7. #    ModalList.c - Main Segment
  8. #
  9. #    Copyright © 1989 Apple Computer, Inc.
  10. #    All rights reserved.
  11. #
  12. #    Versions:    
  13. #            1.00                     10/89
  14. #            1.01                    06/92
  15. #
  16. #    Components:
  17. #            ModalList.make            October 1, 1989
  18. #            ModalList.h                October 1, 1989
  19. #            ModalList.c                October 1, 1989
  20. #            ModalListInit.c            June 12, 1992
  21. #            ModalList.r                October 1, 1989
  22. #            TCModalList.π            June 12, 1992
  23. #            TCModalList.π.rsrc        June 12, 1992
  24. #
  25. #    ModalList is an example application that demonstrates
  26. #    how to use the Dialog Manager and List Manager routines
  27. #    together. It is not a good example of a sample application
  28. #    but a great example of the use of lists in a dialog. The
  29. #    default LDEF is used to display a 2 dimensional list of strings.
  30. #    Each cell's string is initialized to represent it's position in the
  31. #    list. The user can change these strings and search for a particular
  32. #    setting. Once again this is not meant as an example application and
  33. #    there are some features that are documented in the source listing
  34. #    that you should pay close attention to inorder to understand how
  35. #    this example works.
  36. #
  37. */
  38.  
  39. #pragma segment Main
  40.  
  41. #include <QuickDraw.h>
  42. #include <Fonts.h>
  43. #include <Controls.h>
  44. #include <Dialogs.h>
  45. #include <Windows.h>
  46. #include <TextEdit.h>
  47. #include <Memory.h>
  48. #include <Lists.h>
  49. #include <SegLoad.h>
  50. #include <Packages.h>
  51. #include <TextUtils.h>
  52. #include "ModalList.h"
  53.  
  54. /* Here are the prototypes.
  55. */
  56.  
  57. Boolean                MyList(void);                                /* Modal Dialog loop */
  58.  
  59. pascal void            MyItem(DialogPtr, short);                    /* Dialog Manger call backs */
  60. pascal Boolean        MyFilter(DialogPtr, EventRecord *, short *);
  61.  
  62. pascal Boolean        MyListClickLoop(void);                         /* List Manger call backs */
  63. pascal short        MySearch(Ptr, Ptr, short, short);
  64.  
  65. void                LDeselect(ListHandle);                        /* utility routines */
  66. long                Cell2String(Point, char *);
  67.  
  68. /* _DataInit() is part of the MPW runtime library. This external
  69. ** reference to it is done so that its segment can be unloaded, %A5Init.
  70. */
  71. #ifndef THINK_C
  72. extern void _DataInit();
  73. #endif
  74.  
  75. main()
  76. {
  77. #ifndef THINK_C
  78.     UnloadSeg((Ptr) _DataInit);        /* note that _DataInit must not be in Main! */
  79. #endif
  80.     
  81.     MaxApplZone();                    /* expand the heap so code segments load at the top */
  82.  
  83.     Initialize();
  84.     UnloadSeg((Ptr) Initialize);    /* note that Initialize must not be in Main! */
  85.  
  86.     (void) MyList();                /* this sample ignores the return value */
  87. }
  88.  
  89.  
  90. /* a utility routine to deselect all selected cells in the list.
  91. */
  92.  
  93. void LDeselect(ListHandle list)
  94. {
  95.     auto    Point        cell;
  96.     
  97.     /* start with the upper left cell
  98.     */
  99.     SetPt(&cell, 0, 0);
  100.     
  101.     /* for each selected cell
  102.     */
  103.     while(LGetSelect(true, &cell, list))
  104.         
  105.         /* deselect and unhilite it
  106.         */
  107.         LSetSelect(false, cell, list);
  108. }
  109.  
  110. /* a click loop procedure that does nothing but prove it can be done.
  111. */
  112.  
  113. pascal Boolean MyListClickLoop(void)
  114. {
  115.     return(true);
  116. }
  117.  
  118. /* a custom search procedure that is case sensitive. the standard list search procedure
  119. ** uses IUMagIDString which is case insensitive.
  120. ** Parameters:    Ptr        aPtr;        the cell data
  121. **                Ptr        bPtr;        the search pattern
  122. **                short    aLen, bLen;    the lengths
  123. */
  124.  
  125. pascal short MySearch(Ptr aPtr, Ptr bPtr, short aLen, short bLen)
  126. {
  127.     return (0 != IUMagString(aPtr, bPtr, aLen, bLen));
  128. }
  129.  
  130. /* this routine will be called by the Dialog Manager to draw the outline of the
  131. ** default button and the list. 
  132. */
  133.  
  134. pascal void MyItem(DialogPtr dialog, short itemNo)
  135. {
  136.     auto    short        itemType;
  137.     auto    Handle        itemHandle;
  138.     auto    Rect        itemRect;
  139.     
  140.     auto    ListHandle    list;
  141.     
  142.     GetDialogItem(dialog, itemNo, &itemType, &itemHandle, &itemRect);
  143.     switch(itemNo) {
  144.     
  145.     /* outline the default button (see IM I-407).  in this case it is the OK button.
  146.     ** this lets the user know that pressing the return will have the same effect as 
  147.     ** clicking this button.
  148.     */
  149.     case cOKOutlineItem :
  150.         PenSize(3, 3);
  151.         InsetRect(&itemRect, -4, -4);
  152.         FrameRoundRect(&itemRect, 16, 16);
  153.         PenSize(1, 1);
  154.         break;
  155.     
  156.     /* draw the list. the List Manager will draw the cells and scrollbars but does not
  157.     ** draw a border around the list's view rectangle, so it is done here also.
  158.     */
  159.     case cListItem :
  160.  
  161.         /* recover the list handle. it was stuffed into the dialog window's refCon
  162.         ** field when is was created.
  163.         */
  164.         list = (ListHandle) ((DialogPeek)dialog)->window.refCon;
  165.         
  166.         /* let the List Manager draw the list
  167.         */
  168.         LUpdate(dialog->visRgn, list);
  169.  
  170.         /* drawn the lists framing rectangle OUTSIDE the view rectangle.
  171.         ** if the frame is drawn inside the view rectangle then these lines
  172.         ** will be erased, drawn onto or scrolled by the List Manager since the lines
  173.         ** are within the rectangle LM expects to be able to draw in.
  174.         */
  175.         InsetRect(&itemRect, -1, -1);
  176.         FrameRect(&itemRect);
  177.         break;
  178.     }
  179. }
  180.  
  181. /* we need to be able to process mouse clicks in the list. the Dialog Manager makes this 
  182. ** possible through filter procedures like this one. since the default filter procedure
  183. ** will be replaced we also need to handle return key presses.
  184. */
  185.  
  186. pascal Boolean MyFilter(DialogPtr dialog, EventRecord *event, short *itemHit)
  187. {
  188.     auto    ListHandle    list;
  189.     auto    Point        cell;
  190.     auto    char        character;
  191.     auto    Point        where;
  192.     auto    Rect        itemRect;
  193.     auto    short        itemType;
  194.     auto    Handle        itemHandle;
  195.     auto    Str255        string;
  196.     auto    short        length;
  197.  
  198.     switch (event->what) {
  199.     
  200.     /* watch for mouse clicks in the List
  201.     */
  202.     case mouseDown :
  203.         GetDialogItem(dialog, cListItem, &itemType, &itemHandle, &itemRect);
  204.         where = event->where;
  205.         GlobalToLocal(&where);
  206.         
  207.         /* if the user has clicked in the list then we'll handle the processing here
  208.         */
  209.         if (PtInRect(where, &itemRect)) {
  210.         
  211.             /* recover the list handle. it was stuffed into the dialog window's refCon
  212.             ** field when it was created.
  213.             */
  214.             list = (ListHandle) ((DialogPeek)dialog)->window.refCon;
  215.             
  216.             /* let the List Manager process the mouse down. this includes cell selection
  217.             ** dragging, scrolling and double clicks by the user.
  218.             */
  219.             if (LClick(where, event->modifiers, list)) {
  220.             
  221.                 /* a double click in a cell has occured.
  222.                 ** find out in which one of the cells the user has double clicked in.
  223.                 */
  224.                 cell = LLastClick(list);
  225.                 
  226.                 /* copy this cell's contents to the text edit box in the dialog
  227.                 */
  228.                 GetDialogItem(dialog, cTextItem, &itemType, &itemHandle, &itemRect);
  229.                 length = 255;
  230.                 LGetCell(&string[1], &length, cell, list);
  231.                 string[0] = (char) length;
  232.                 SetDialogItemText(itemHandle, string);
  233.             }
  234.             
  235.             /* tell the application that the list has been clicked in.
  236.             */
  237.             *itemHit = cListItem;
  238.             
  239.             /* tell the Dialog Manager that the event has been handled.
  240.             */
  241.             return true;
  242.         }
  243.         break;
  244.     
  245.     /* be sure and return this information so the Dialog Manager will process the 
  246.     ** return and enter key presses as clicks by the user in the OK button. this is
  247.     ** only required because we have overridden the Dialog Manager's default filtering.
  248.     */
  249.     case keyDown :    
  250.     case autoKey :
  251.         character = event->message;
  252.         if ('\n' == character) {
  253.  
  254.             /* tell the application that the OK button has been clicked by the user.
  255.             */
  256.             *itemHit = ok;
  257.             
  258.             /* tell the Dialog Manger that the event has been handled and the
  259.             ** character should not be added to the text edit field.
  260.             */
  261.             return true;
  262.         }
  263.         break;
  264.     }
  265.     
  266.     /* tell the Dialog Manger that the event has NOT been handled and that it should
  267.     ** take further action on this event.
  268.     */
  269.     return false;
  270. }
  271.  
  272. /* this routine will not return until the user has dismissed the modal dialog with
  273. ** a press of the return key or a click in the ok or cancel buttons. it displays a 
  274. ** list, check boxes for setting selection flags, a text edit field for searching and setting
  275. ** cell data and the usual ok and cancel buttons. the list uses the default LDEF procedure.
  276. **
  277. ** if you double click in a cell the cells contents will be moved to the
  278. ** text edit field. edit the field and click the set button and you will have edited the
  279. ** cell contents.
  280. **
  281. ** note that the Set button sets the last cell clicked in to the contents of the text edit field.
  282. ** it does not necessarily set the cell which is selected or hilited but it sets the cell that
  283. ** LLastClick returns. it is sort of a funky user interface but it does demonstrate the
  284. ** use of LLastClick.
  285. **
  286. ** if you scroll the list all the way to the right you will notice that there is some extra
  287. ** white space to the right of the right most cell. this was done intentionally to show what
  288. ** can happen when the lists viewing rectangle is not an even multiple of the cell rectangles.
  289. ** the List Manager must scroll this extra bit so that it can display the entire width of the
  290. ** right most cells. notice too that the bottom most cell does not display this behavior since
  291. ** the height of the list's viewing rectangle is an even multiple of the cell height.
  292. **
  293. ** note that a call to LSetSelect will select or hilite a cell which is empty even though 
  294. ** the list's selection flags field is set to lNoNilHilite. the selection flags
  295. ** are used only by the LClick routine they do not prevent the program from selecting 
  296. ** cells with LSetSelect in such a way that may seem inconsistent with their
  297. ** setting. this peculiarity can also happen with other selection flag settings and the
  298. ** LSetSelect call.
  299. **
  300. ** notice also that the column widths are all equal, there is no way to make cells with different 
  301. ** column widths using the List Manager.
  302. **
  303. ** there have been requests for the capability to have multiple lists displayed in a
  304. ** window and have them both scrolled by one scrollbar. the only way to accomplish this 
  305. ** would be to make new lists without the List Manager's scrollV or scrollH scrollbars.
  306. ** then create a new scrollbar using the Control Manager and setup an action procedure for
  307. ** TrackControl which calls LScroll for each of the lists. this is not demonstrated in
  308. ** this sample program.
  309. **
  310. */
  311.  
  312. RoutineDescriptor MyListClickLoopRD = BUILD_ROUTINE_DESCRIPTOR (uppListClickLoopProcInfo, MyListClickLoop);
  313. // LClickLoopGlue is a quick & dirty way of doing some inline 68K assembly from PowerPC code.
  314. // We need to do this because if we have the List Manager call our native click loop directly,
  315. // then it can fail because the List Manager doesn't actually test the result in D0, it just
  316. // checks the state of the Z-bit, which Mixed Mode doesn't set for us.
  317. #ifdef powerc
  318. #pragma options align=mac68k
  319. #endif
  320. struct LClickLoopGlue {
  321.     long                move;            // MOVEA.L    ClickLoopUPP, A0
  322.     short                jsr;            // JSR        (A0)
  323.     short                 tst;            // TST.B    D0
  324.     short                rts;            // RTS
  325.     UniversalProcPtr    ClickLoopUPP;    // Storage for the UPP
  326. } LClickLoop68K = {
  327.     0x207A0008,
  328.     0x4E90,
  329.     0x4A00,
  330.     0x4E75,
  331.     &MyListClickLoopRD
  332. };
  333. #ifdef powerc
  334. #pragma options align=reset
  335. #endif
  336.  
  337. Boolean MyList(void)
  338. {
  339.     auto    DialogPtr    dialog;            /* a dialog containing the list item */
  340.  
  341.     auto    short        itemNo;            /* the item in the dialog selected */
  342.     auto    short        itemType;        /* dummy parameter for call to GetDialogItem */
  343.     auto    Handle        itemHandle;        /* dummy parameter for call to GetDialogItem */
  344.     auto    Rect        itemRect;         /* the location of the list in the dialog */
  345.     
  346.     auto    ListHandle    list;            /* the list constructed in the dialog */
  347.     auto    Rect        dataBounds;        /* the dimensions of the data in the list */
  348.     auto    Point        cellSize;        /* width and height of a cells rectangle */
  349.     auto    Point        cell;            /* an index through the list */
  350.  
  351.     auto    char        string[255];
  352.     auto    short        length;
  353.     
  354.     auto    short        checked;        /* flag for setting and getting check box value */
  355.     auto    short        bit;            /* used as a mask to test the selection flags */ 
  356.     
  357.     auto    ModalFilterUPP mfUPP;
  358.     
  359.     /* create a new dialog window
  360.     */
  361.     dialog = GetNewDialog(cDLOGID, (Ptr) 0, (WindowPtr) -1);
  362.     SetPort((GrafPtr) dialog);
  363.         
  364.     /* set the procedure pointer for the user items in the dialog.
  365.     ** this will allow he default button to be outlined and the list to be drawn
  366.     ** by the Dialog Manger.
  367.     */
  368.     GetDialogItem(dialog, cOKOutlineItem, &itemType, &itemHandle, &itemRect);
  369.     SetDialogItem(dialog, cOKOutlineItem, itemType, (Handle) MyItem, &itemRect);
  370.     
  371.     GetDialogItem(dialog, cListItem, &itemType, &itemHandle, &itemRect);
  372.     SetDialogItem(dialog, cListItem, itemType, (Handle) MyItem, &itemRect);
  373.  
  374.     /* make room for scroll bars (see IM IV-270)
  375.     */
  376.      itemRect.right -= 15;
  377.      itemRect.bottom -= 15;
  378.  
  379.     /* create a list
  380.     */
  381.     SetRect(&dataBounds, 0, 0, cListCols, cListRows);
  382.     SetPt(&cellSize, cListCellWidth, cListCellHeight);
  383.     list = LNew(&itemRect, &dataBounds, cellSize, 0, (WindowPtr) dialog, false, false, true, true);
  384.  
  385.     /* allow the dialog manager routines to access the list record
  386.     */
  387.     ((DialogPeek)dialog)->window.refCon = (long) list;
  388.     
  389.     /* use the custom click loop procedure
  390.     */
  391.     (*list)->lClickLoop = (ListClickLoopUPP)&LClickLoop68K;
  392.  
  393.     /* use the default selection flags
  394.     */
  395.     (*list)->selFlags = 0;
  396.     
  397.     /* initialize the check boxes in the dialog according
  398.     ** to the settings of the list's selection flags
  399.     */
  400.     bit = 2;
  401.     itemNo = cNoNilHiliteItem;
  402.     while (itemNo <= cOnlyOneItem) {
  403.     
  404.         GetDialogItem(dialog, itemNo, &itemType, &itemHandle, &itemRect);
  405.          checked = ((*list)->selFlags == ((*list)->selFlags | bit)) ? 1 : 0;
  406.          SetControlValue((ControlHandle) itemHandle, checked);
  407.         
  408.         bit *= 2;
  409.         ++itemNo;
  410.        }
  411.  
  412.     /* initialize the cell's contents.
  413.     */
  414.     for (cell.v = 0; cell.v < (**list).dataBounds.bottom; ++cell.v) {
  415.         for (cell.h = 0; cell.h < (**list).dataBounds.right; ++cell.h) {
  416.         
  417.             /* make a string representing the cell's position in the list.
  418.             */
  419.             length = Cell2String(cell, string);
  420.             
  421.             /* initialize the cell contents to this string.
  422.             **
  423.             ** you would initialize your cell data with what ever is appropriate
  424.             ** for your application here.
  425.             */
  426.             LSetCell(string, length, cell, list);
  427.         }
  428.     }
  429.     
  430.     /* turn cell drawing on only after the cell contents have been initialized.
  431.     ** this will avoid watching the delay between the LSetCells calls and is faster.
  432.     */
  433.     LSetDrawingMode(true, list);
  434.     
  435.     mfUPP = NewModalFilterProc(MyFilter);
  436.     do {
  437.     
  438.         /* process hits in the dialog.
  439.         */
  440.         ModalDialog(mfUPP, &itemNo);
  441.         
  442.         switch(itemNo) {
  443.  
  444.         /* process hits in the OK button.
  445.         */ 
  446.         case ok :
  447.         
  448.             /* find out which cells have been selected.
  449.             */
  450.             SetPt(&cell, 0, 0);
  451.             while(LGetSelect(true, &cell, list)) {
  452.             
  453.                 /* there is nothing to do with the user's selections in this sample
  454.                 ** so i'll just deselect the cells the users has selected.
  455.                 */
  456.                 LSetSelect(false, cell, list);
  457.             }
  458.             break;
  459.         
  460.         /* process hits in the Find button.
  461.         */ 
  462.         case cFindItem :
  463.         
  464.             GetDialogItem(dialog, cTextItem, &itemType, &itemHandle, &itemRect);
  465.             GetDialogItemText(itemHandle, string);
  466.             
  467.             /* search for a match in the list.
  468.             ** start the search at the upper left most cell in the list.
  469.             **
  470.             ** the List Manger performs the searchs row by row until it finds
  471.             ** the first match. this order can not be changed without writing a
  472.             ** replacement to LSearch, probably making use of LGetCell, LNextCell and
  473.             ** the International search routines.
  474.             */
  475.             SetPt(&cell, 0, 0);
  476.             
  477.             /* deselect all cells which are currently selected.
  478.             */
  479.             LDeselect(list);
  480.                 
  481.             /* use the custom search procedure.
  482.             ** the default search procedure is not case sensitive.
  483.             */
  484.             if (LSearch(&string[1], string[0], MySearch, &cell, list)) {
  485.             
  486.                 /* hilite the cell which contains the matching string.
  487.                 **
  488.                 ** note that this call will select or hilite a cell which is empty
  489.                 ** when the users search string is empty even though the list's
  490.                 ** selection flags field is set to lNoNilHilite. the selection flags
  491.                 ** are used only by the LClick routine they do not prevent the program
  492.                 ** from selecting cells with LSetSelect in such a way that may seem 
  493.                 ** inconsistent with their setting. this peculiarity can also happen
  494.                 ** with other selection flag settings and the LSetSelect call.
  495.                 */
  496.                 LSetSelect(true, cell, list);
  497.                 
  498.                 /* if the matching cell is not visible it will be scrolled into view.
  499.                 */
  500.                 LAutoScroll(list);
  501.             }
  502.             break;
  503.             
  504.         /* process hits in the Set button.
  505.         */ 
  506.         case cSetItem :
  507.             GetDialogItem(dialog, cTextItem, &itemType, &itemHandle, &itemRect);
  508.             GetDialogItemText(itemHandle, string);
  509.             
  510.             /* find out which was the last cell clicked in by the user.
  511.             ** not the last cell dragged into, or the last cell selected but the
  512.             ** last cell a mouse down event occured in.
  513.             */
  514.             cell = LLastClick(list);
  515.             if (0 <= cell.h && 0 <= cell.v) {
  516.             
  517.                 
  518.                 /* if a cell has been clicked in then deselect all cells which are
  519.                 ** currently selected.
  520.                 */
  521.                 LDeselect(list);
  522.  
  523.                 /* set the cell to the string the user has entered.
  524.                 */
  525.                 LSetCell(&string[1], string[0], cell, list);
  526.                 
  527.                 /* notice that this will Hilite an empty cell even with
  528.                 ** the list's selection flags set to lNoNilHilite.
  529.                 */
  530.                 LSetSelect(true, cell, list);
  531.  
  532.                 /* if the last cell clicked in is not visible it will be scrolled
  533.                 ** into view.
  534.                 */
  535.                 LAutoScroll(list);
  536.             }
  537.             break;
  538.             
  539.         /* process hits in the check boxes representing the list's selection flags.
  540.         */ 
  541.         case cOnlyOneItem :
  542.         case cExtendDragItem :
  543.         case cNoDisjointItem :
  544.         case cNoExtendItem :
  545.         case cNoRectItem :
  546.         case cUseSenseItem :
  547.         case cNoNilHiliteItem :
  548.         
  549.             /* deselect all cells which are currently selected.
  550.             */
  551.             LDeselect(list);
  552.             
  553.             /* toggle the check box.
  554.             */
  555.             GetDialogItem(dialog, itemNo, &itemType, &itemHandle, &itemRect);
  556.               checked = GetControlValue((ControlHandle) itemHandle);
  557.               SetControlValue((ControlHandle) itemHandle, !checked);
  558.  
  559.             /* adjust the selection flags to the users new settings.
  560.             */
  561.             switch (itemNo) {
  562.             case cOnlyOneItem :
  563.                 (*list)->selFlags ^= lOnlyOne; break;
  564.             case cExtendDragItem :
  565.                 (*list)->selFlags ^= lExtendDrag; break;
  566.             case cNoDisjointItem :
  567.                 (*list)->selFlags ^= lNoDisjoint; break;
  568.             case cNoExtendItem :
  569.                 (*list)->selFlags ^= lNoExtend; break;
  570.             case cNoRectItem :
  571.                 (*list)->selFlags ^= lNoRect; break;
  572.             case cUseSenseItem :
  573.                 (*list)->selFlags ^= lUseSense; break;
  574.             case cNoNilHiliteItem :
  575.                 (*list)->selFlags ^= lNoNilHilite; break;
  576.             }
  577.  
  578.             break;
  579.         }                
  580.     } while ((itemNo != ok) && (itemNo != cancel));
  581.     DisposeRoutineDescriptor(mfUPP);
  582.     
  583.     /*  kill the list and dialog.
  584.     */
  585.     LDispose(list);
  586.     DisposeDialog(dialog);
  587.     
  588.     /* return true if OK or RETURN key was hit.
  589.     */
  590.     return (ok == itemNo);
  591. }
  592.  
  593. /* just a funky routine to create strings for the cell's data
  594. */
  595.  
  596. long Cell2String(Point pt, char *string)
  597. {
  598.     auto    short        index;
  599.     auto    Str255        rowStr;
  600.     auto    Str255        colStr;
  601.     
  602.     NumToString((long)pt.v, rowStr);
  603.     *(string)++ = 'R';
  604.     for (index = 1; index <= rowStr[0]; ++index)
  605.         *(string++) = rowStr[index];
  606.         
  607.     *(string)++ = ',';
  608.     *(string)++ = ' ';
  609.  
  610.     NumToString((long)pt.h, colStr);
  611.     *(string)++ = 'C';
  612.     for (index = 1; index <= colStr[0]; ++index) 
  613.         *(string++) = colStr[index];
  614.     
  615.     return colStr[0] + rowStr[0] + 4;
  616. }
  617.